package hk.linkcredit.walletservice.wallet.controller;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import hk.linkcredit.walletservice.constant.Status;
import hk.linkcredit.walletservice.constant.WalletConstants;
import hk.linkcredit.walletservice.exception.BizException;
import hk.linkcredit.walletservice.model.Result;
import hk.linkcredit.walletservice.utils.core.RedisCache;
import hk.linkcredit.walletservice.wallet.domain.vo.*;
import hk.linkcredit.walletservice.wallet.service.*;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.Date;

/**
 * @Author: Jamnsin
 * @Date: 2022/6/10 01:31
 */
@Slf4j
@RestController
@RequestMapping("/api")
public class ApiController {

    @Autowired
    WalletAccountService walletAccountService;

    @Autowired
    WalletOrderService walletOrderService;

    @Autowired
    WalletFlowService walletFlowService;

    @Autowired
    WalletService walletService;

    @Autowired
    AsyncService asyncService;

    @Autowired
    RedisCache redisCache;

    @PostMapping("/{custNo}/account/init")
    public Result initWalletAccount(@PathVariable String custNo) {
        walletAccountService.initWalletAccountAll(custNo);
        return Result.success();
    }

    @GetMapping("/{custNo}/account")
    public Result getWalletAccount(@PathVariable String custNo) {
        WalletAccountVo walletAccount = walletAccountService.getWalletAccount(custNo);
        return Result.success(walletAccount);
    }

    @PostMapping("/{custNo}/account")
    public Result updateWalletAccount(@PathVariable String custNo, @RequestBody @Validated WalletAccountReq walletAccountReq) {
        String lockKey = WalletConstants.WALLET_ACCOUNT_LOCK_KEY_PREFIX + walletAccountReq.getCustNo();
        RLock lock = redisCache.lock(lockKey);
        try {
            long start = System.currentTimeMillis();
            walletAccountService.updateWalletAccount(walletAccountReq);

            WalletAccountMsg walletAccountMsg = WalletAccountMsg.builder().custNo(walletAccountReq.getCustNo()).walletType(walletAccountService.getWalletType(walletAccountReq.getFundsType())).changeType(WalletConstants.WALLET_FLOW_INCOME).fundsType(walletAccountReq.getFundsType()).changeAmount(walletAccountReq.getAmount()).changeTime(new Date()).build();
            asyncService.sendWalletAccountAmountMessage(walletAccountMsg);

            log.info("锁占用时间，耗时：{} ms [ {} ]", (System.currentTimeMillis() - start), lockKey);
        } catch (Exception e) {
            log.error("更新钱包账户异常：", e);
            throw new BizException(Status.WALLE_OPERATE_ERROR);
        } finally {
            lock.unlock();
        }

        return Result.success();
    }

    @PostMapping("/{custNo}/order/{orderType}")
    public Result createOrder(@PathVariable("custNo") String custNo, @PathVariable("orderType") String orderType, @RequestBody JSONObject orderReq) {

        switch (orderType) {
            case "4":
            case "5":
                JSONObject withdrawRst = dealWalletWithdraw(orderType, orderReq);
                return Result.success(withdrawRst);
            case "6":
            case "7":
                JSONObject repayRst = dealWalletRepay(orderType, orderReq);
                return Result.success(repayRst);
            default:
                throw new BizException(Status.WALLE_ORDER_TYPE_CANNOT_SUPPORT);
        }

    }

    @PostMapping("/order")
    public Result updateOrder(@RequestBody @Validated WalletOrderReq walletOrderReq) {

        String lockKey = WalletConstants.WALLET_ACCOUNT_LOCK_KEY_PREFIX + walletOrderReq.getCustNo();
        RLock lock = redisCache.lock(lockKey);
        try {
            walletService.updateWalletOrder(walletOrderReq);
        } catch (BizException e) {
            log.error("更新订单状态异常，", e.getMessage());
            throw e;
        } catch (Exception e) {
            log.error("更新订单状态 error：", e);
            throw e;
        } finally {
            lock.unlock();
        }

        return Result.success();
    }

    @GetMapping("/{custNo}/account/info")
    public Result getWalletAccountInfo(@PathVariable String custNo) {
        WalletAccountInfo walletAccount = walletAccountService.getWalletAccountInfo(custNo);
        return Result.success(walletAccount);
    }

    /**
     * @param custNo
     * @param flowType income/expend
     * @return
     */
    @GetMapping("/{custNo}/flow/{flowType}")
    public Result getWalletFlow(@PathVariable(name = "custNo") String custNo, @PathVariable(name = "flowType") String flowType) {

        if (WalletConstants.WALLET_FLOW_INCOME.equals(flowType)) {
            return Result.success(walletFlowService.getWalletIncome(custNo, WalletConstants.WALLET_FLOW_INCOME));
        }

        if (WalletConstants.WALLET_FLOW_EXPEND.equals(flowType)) {
            return Result.success(walletFlowService.getWalletExpend(custNo));
        }

        return Result.success();
    }

    /**
     * @param walletStatusReq
     * @return
     */
    @PostMapping("/status")
    public Result updateWalletStatus(@RequestBody @Validated WalletStatusReq walletStatusReq) {
        walletAccountService.updateWalletStatus(walletStatusReq);
        return Result.success();
    }

    @GetMapping("/cust/list/balance")
    public Result getCustomersWithBalance() {
        return Result.success(walletAccountService.getCustomersWithBalance());
    }

    private JSONObject dealWalletWithdraw(String orderType, JSONObject orderReq) {
        log.info("进入处理钱包提现 >>> orderType = {}", orderType);

        WalletWithdrawReq walletWithdrawReq = JSON.parseObject(orderReq.toString(), WalletWithdrawReq.class);

        String lockKey = WalletConstants.WALLET_ACCOUNT_LOCK_KEY_PREFIX + walletWithdrawReq.getCustNo();
        RLock lock = redisCache.lock(lockKey);

        JSONObject withdrawRst;
        try {
            long start = System.currentTimeMillis();
            withdrawRst = walletService.walletWithdraw(orderType, walletWithdrawReq);
            log.info("锁占用时间，耗时：{} ms [ {} ]", (System.currentTimeMillis() - start), lockKey);
        } catch (Exception e) {
            if (e instanceof BizException) {
                log.error("钱包提现异常 [BizException]，custNo：{}，error：", walletWithdrawReq.getCustNo(), e.getMessage());
                throw e;
            }
            log.error("钱包提现异常，custNo：{}，error：", walletWithdrawReq.getCustNo(), e);
            throw new BizException(Status.WALLE_WITHDRAW_ERROR);
        } finally {
            lock.unlock();
        }

        log.info("进入处理钱包提现 >>>");
        return withdrawRst;
    }

    private JSONObject dealWalletRepay(String orderType, JSONObject orderReq) {
        log.info("进入处理钱包还款 >>> orderType = {}", orderType);

        WalletRepayReq walletRepayReq = JSON.parseObject(orderReq.toString(), WalletRepayReq.class);
        String lockKey = WalletConstants.WALLET_ACCOUNT_LOCK_KEY_PREFIX + walletRepayReq.getCustNo();
        RLock lock = redisCache.lock(lockKey);

        JSONObject repayRst;
        try {
            long start = System.currentTimeMillis();
            repayRst = walletService.walletRepay(orderType, walletRepayReq);

            log.info("锁占用时间，耗时：{} ms [ {} ]", (System.currentTimeMillis() - start), lockKey);
        } catch (Exception e) {
            if (e instanceof BizException) {
                log.error("钱包还款异常 [BizException]，custNo：{}，error：", walletRepayReq.getCustNo(), e.getMessage());
                throw e;
            }
            log.error("钱包还款异常，客户号：{}，error：", walletRepayReq.getCustNo(), e);
            throw new BizException(Status.WALLE_REPAYMENT_ERROR);
        } finally {
            lock.unlock();
        }

        log.info("进入处理钱包还款 >>>");
        return repayRst;
    }

}
